/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package genomemap.data;

import javautil.collections.ArrayUtil;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;

/**
 * @author Susanta Tewari
 * @version 1.0
 * @created 16-Nov-2007 7:15:52 PM
 */
public class JDataSource {

    /**
     * the gentic data source
     */
    private GDataSource gData = null;

    /**
     * contains Gene objects.
     */
    private final LinkedHashSet<Gene> geneSet = new LinkedHashSet<>();

    /**
     * pData: the physical data in the joint data computation gData: similarly defined
     * for genetic data
     */
    private PDataSource pData = null;

    /**
     * contains Probe objets
     */
    private LinkedHashSet<Probe> probeSet = new LinkedHashSet<>();

    /** Field description */
    public boolean dataLoaded = false;

    /**
     * any acceptable gene order is fine in the <CODE>gData</CODE> since all the genes
     * prsent in the chromosome will be searched to find the biggests et of tagged genes
     * and ony those will be used in the genetic lhood computation as a contributing part
     * of the joint likelihood computation.
     * @param gData
     * @param pData
     */
    public JDataSource(GDataSource gData, PDataSource pData) {

        setGData(gData);
        setPData(pData);
        setOrder(pData.getOrder());
    }

    /**
     * call this only after setting PDataSource and GDataSource
     */
    public void loadData() {

        if (!dataLoaded) {

            gData.loadData();
            pData.loadData();

            dataLoaded = true;
        }

        probeSet = pData.getProbeSet();

        if (geneSet.isEmpty()) {

            LinkedHashSet<Gene> allGenes = gData.getFullGeneSet();

            for (Gene gene : allGenes) {

                if (gene.hasSeqInfo()) {
                    this.geneSet.add(gene);
                }
            }

            if (geneSet.size() % 2 == 0) {
                geneSet.remove(geneSet.iterator().next());
            }

        } else {


            // remove tagged probes from all genes
            Iterator<Gene> itr = geneSet.iterator();

            while (itr.hasNext()) {

                Gene gene = itr.next();

                gene.getTaggedCloneSet().clear();
            }
        }


        // configure tagging among probes and genes
        Iterator<Probe> itrProbe = probeSet.iterator();

        while (itrProbe.hasNext()) {

            Probe probe = itrProbe.next();

            if (probe.hasSeqInfo()) {

                Iterator<Gene> itrGene = geneSet.iterator();

                while (itrGene.hasNext()) {

                    Gene gene = itrGene.next();

                    if (!gene.isTagged() && probe.isOverLapping(gene)) {

                        gene.addTaggedClone(probe);
                        probe.addTaggedGene(gene);

                        break;
                    }
                }
            }
        }


        // set the gene order as the data is loaded
        gData.setOrder(getGeneOrder(pData.getOrder()));
    }

    /**
     * if newProbeOrder is not a sub-sequence then re-load data to refresh the probe and gene sets
     * @param newProbeOrder
     * @param probeOrder
     */
    public void setOrder(int[] newProbeOrder) {

        int[] oldProbeOrder = null;

        if (pData.getOrder() != null) {
            oldProbeOrder = pData.getOrder().clone();
        }

        pData.setOrder(newProbeOrder);

        if (dataLoaded && (oldProbeOrder != null)) {

            if (!ArrayUtil.contains(oldProbeOrder, newProbeOrder)) {

                loadData();

                return;
            }

            gData.setOrder(getGeneOrder(newProbeOrder));
        }
    }

    /**
     * should be called only after <CODE>loadData()</CODE> has been called
     * at least once.
     *
     * @param probeOrder description
     *
     * @return description
     */
    private int[] getGeneOrder(int[] probeOrder) {

        Gene[] geneArray = geneSet.toArray(new Gene[0]);
        boolean[] placed = new boolean[geneArray.length];    // default is false

        for (int geneIndex = 0; geneIndex < geneArray.length; geneIndex++) {


            // for genes that are not tagged  will remain in place and hence placed wiill be true for them
            if (geneArray[geneIndex].isTagged()) placed[geneIndex] = true;
        }


        // order the probes into probeArray
        List<Probe> reqProbeList = new ArrayList<>();

        for (int probeIndex1 = 0; probeIndex1 < probeOrder.length; probeIndex1++) {

            for (Iterator<Probe> itr = probeSet.iterator(); itr.hasNext(); ) {

                Probe probe = itr.next();

                if (probeOrder[probeIndex1] == probe.getOrder()) {
                    reqProbeList.add(probe);
                }
            }
        }


        // order the genes into geneArray
        for (int probeIndex = 0; probeIndex < reqProbeList.size(); probeIndex++) {

            Probe probe = reqProbeList.get(probeIndex);

            if (probe.isTagged()) {

                for (int geneIndex = 0; geneIndex < geneArray.length; geneIndex++) {

                    if (!placed[geneIndex] && geneArray[geneIndex].isTagged()) {

                        Gene gene = probe.getTaggedGeneSet().iterator().next();

                        placed[geneIndex]    = true;
                        geneArray[geneIndex] = gene;

                        break;
                    }
                }
            }
        }


        // a check: if there is any false in placed, that signals an error
        // all genes that were tagged tagged must have been claimed by their
        // corresponding probes their probe order. failing to do so indicates data integrity loss
//      for (int i = 0; i < placed.length; i++) {
//          if(!placed[i]){
//              System.out.println("From jdatasource.getGeneOrder(): ");
//              System.out.println(":Some tagged genes not claimed by their probes..");
//              System.out.println("possible data integrity loss..");
//              System.exit(0);
//          }
//
//      }
        int[] geneOrder = new int[geneArray.length];

        for (int geneIndex = 0; geneIndex < geneArray.length; geneIndex++) {

            geneOrder[geneIndex] = geneArray[geneIndex].getOrder();
        }

        return geneOrder;
    }

    /**
     * the gentic data source
     */
    public GDataSource getGData() {
        return gData;
    }

    /**
     * the physical data in the joint data computation
     */
    public PDataSource getPData() {
        return pData;
    }

    /**
     * the genetic data in the joint data computation
     *
     * @param newVal
     */
    void setGData(GDataSource newVal) {
        gData = newVal;
    }

    /**
     * the physical data in the joint data computation
     *
     * @param newVal
     */
    void setPData(PDataSource newVal) {
        pData = newVal;
    }
}
